package org.pentaho.obscommon;

import com.obs.services.*;
import com.obs.services.internal.security.BasicSecurityKey;
import com.obs.services.model.ISecurityKey;
import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.provider.AbstractFileName;
import org.apache.commons.vfs2.provider.AbstractFileSystem;
import org.pentaho.di.connections.ConnectionDetails;
import org.pentaho.di.connections.ConnectionManager;
import org.pentaho.di.core.encryption.Encr;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.huaweicloud.obs.OBSUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

public abstract class OBSCommonFileSystem extends AbstractFileSystem {

    private static final Logger logger = LoggerFactory.getLogger( OBSCommonFileSystem.class );
    private LogChannelInterface log = new LogChannel( this );
    private static final String DEFAULT_OBS_CONFIG_PROPERTY = "defaultOBSConfig";
    private String hwAccessKeyCache;
    private String hwSecretKeyCache;
    IObsClient client;
    private final Supplier<ConnectionManager> connectionManager = ConnectionManager::getInstance;
    private Map<String, String> currentConnectionProperties;
    private FileSystemOptions currentFileSystemOptions;

    protected OBSCommonFileSystem(final FileName rootName, final FileSystemOptions fileSystemOptions) {
        super(rootName, null, fileSystemOptions);
        currentConnectionProperties = new HashMap<>();
    }

    protected void addCapabilities(Collection caps) {
        caps.addAll(OBSCommonFileProvider.capabilities);
    }

    protected abstract FileObject createFile(AbstractFileName var1) throws Exception;


    public IObsClient getOBSClient() {

        OBSCommonFileSystemConfigBuilder obsCommonFileSystemConfigBuilder =
                new OBSCommonFileSystemConfigBuilder( getFileSystemOptions() );

        Optional<? extends ConnectionDetails> defaultOBSConnection = Optional.empty();
        try {
            defaultOBSConnection =
                    connectionManager.get().getConnectionDetailsByScheme( "obs" ).stream().filter(
                                    connectionDetails -> connectionDetails.getProperties().get( DEFAULT_OBS_CONFIG_PROPERTY ) != null
                                            && connectionDetails.getProperties().get( DEFAULT_OBS_CONFIG_PROPERTY ).equalsIgnoreCase( "true" ) )
                            .findFirst();
        } catch ( Exception ignored ) {
            // Ignore the exception, it's OK if we can't find a default OBS connection.
        }

        if ( obsCommonFileSystemConfigBuilder.getName() == null ) {
            // Copy the connection properties
            Map<String, String> newConnectionProperties = new HashMap<>();
            defaultOBSConnection
                    .ifPresent( connectionDetails -> newConnectionProperties.putAll( connectionDetails.getProperties() ) );

            // Have the default connection properties changed?
            if ( !newConnectionProperties.equals( currentConnectionProperties ) ) {
                // Force a new connection if the default PVFS was changed
                client = null;
                // Track the new connection
                currentConnectionProperties = newConnectionProperties;
                // Clear the file system cache as the credentials have changed and the cache is now invalid.
                this.getFileSystemManager().getFilesCache().clear( this );
            }
        }

        if ( currentFileSystemOptions != null && !currentFileSystemOptions.equals( getFileSystemOptions() ) ) {
            client = null;
            this.getFileSystemManager().getFilesCache().clear( this );
        }

        if ( client == null && getFileSystemOptions() != null ) {
            currentFileSystemOptions = getFileSystemOptions();
            String accessKey = null;
            String secretKey = null;
            String endpoint = null;

            if ( obsCommonFileSystemConfigBuilder.getName() == null && defaultOBSConnection.isPresent() ) {
                accessKey = Encr.decryptPassword( currentConnectionProperties.get( "accessKey" ) );
                secretKey = Encr.decryptPassword( currentConnectionProperties.get( "secretKey" ) );
                endpoint = currentConnectionProperties.get( "endpoint" );
            } else {
                accessKey = obsCommonFileSystemConfigBuilder.getAccessKey();
                secretKey = obsCommonFileSystemConfigBuilder.getSecretKey();
                endpoint = obsCommonFileSystemConfigBuilder.getEndpoint();
            }

            OBSUtil.OBSKeys keys = OBSUtil.getKeysFromURI( getRootURI() );
            if ( keys != null ) {
                accessKey = keys.getAccessKey();
                secretKey = keys.getSecretKey();
            }

            BasicSecurityKey credentials = new BasicSecurityKey( accessKey, secretKey );
            ObsConfiguration config = new ObsConfiguration();
            config.setEndPoint( "obs." + endpoint + ".myhuaweicloud.com" );
            client = new ObsClient( new BasicObsCredentialsProvider( credentials ), config );
        }

        return client;
    }
}
